/*
 * Arbol.cpp
 *
 *  Created on: 03/04/2010
 *      Author: administrador
 */

#include "Arbol.h"
#include <iostream>
#include <fstream>
#include "Excepciones.h"
#include "FactoryNodo.h"
#include "Serializable.h"

Arbol* Arbol::instancia = NULL;
int Arbol::tamanioMaximoNodo = 0;
int Arbol::idRaiz = 0;
const float Arbol::porcentajeCargaMaxima = 0.5;
const float Arbol::porcentajeOverflow = 0.8;
const float Arbol::porcentajeUnderflow = 0.4;

Arbol::Arbol(int tamanioMaximo){

	Archivo::setTamanioBloque(tamanioMaximo);

	setTamanioMaximoNodo(tamanioMaximo);

	if (Archivo::getInstance("r+")){

		this->hidratar();
	}
	else{

		Archivo::getInstance()->getBloqueLibre();

		this -> raiz = new NodoHoja();

		this -> raiz->hidratar();

		idRaiz = raiz->getId();

		this -> nodoActual = NULL;

		this -> elementoActual = NULL;

		this -> cantidadNiveles = 1;
	}


	// TODO: Almacenar la raiz en archivo
	// TODO: Crear archivo de referencias a nodos libres vacio
}

Arbol* Arbol::getInstancia() {
	if (instancia == NULL) {

		instancia = new Arbol(tamanioMaximoNodo);
	}
	return instancia;
}

void Arbol::borrarInstancia() {
	delete instancia;
	instancia = NULL;
}

int Arbol::getIdRaiz(){

	return idRaiz;
}

void Arbol::setTamanioMaximoNodo(int tamanio)
{
	tamanioMaximoNodo = tamanio;
	INodo::setTamanioNodo(tamanio);
	Archivo::setTamanioBloque(tamanio);
}

Arbol::~Arbol() {
	this->raiz->serializar();
	this->serializar();
	delete this->raiz;
	if (this->elementoActual){
		delete this->elementoActual;
	}

	if (this->nodoActual){

		delete this->nodoActual;
	}
	Archivo::getInstance()->deleteInstance();

}

//void Arbol::partirRaizEnOverflow(NodoHoja* nodoHijoIzquierdo, NodoHoja* nodoHijoDerecho) {
//	Elemento* elementoActual;
//	int i;
//
//	int elementoMedio = raiz->getCantidadElementos() / 2;
//	for (i = 0; i < elementoMedio; i++) {
//		elementoActual = ((NodoHoja*) raiz) -> getElemento(i);
//		nodoHijoIzquierdo -> agregarElemento(elementoActual);
//	}
//	for (int j = i; j < raiz -> getCantidadElementos(); j++) {
//		elementoActual = ((NodoHoja*) raiz) -> getElemento(j);
//		nodoHijoDerecho -> agregarElemento(elementoActual);
//	}
//	raiz = new NodoIndice(cantidadNiveles, 0, Raiz);
//
//}

//void Arbol::partirRaizEnOverflow(NodoIndice* nodoHijoIzquierdo, NodoIndice* nodoHijoDerecho, int &claveAPromover) {
//	int claveElementoActual;
//	int idNodoActual;
//
//	int i;
//
//	int elementoMedio = raiz->getCantidadElementos() / 2;
//	for (i = 0; i < elementoMedio; i++) {
//		claveElementoActual = ((NodoIndice*) raiz) -> getClaveElemento(i);
//		idNodoActual = ((NodoIndice*) raiz) -> getIdNodo(i);
//
//		nodoHijoIzquierdo -> agregarClaveElemento(claveElementoActual);
//		nodoHijoIzquierdo -> agregarIdNodo(idNodoActual);
//	}
//	idNodoActual = ((NodoIndice*) raiz) -> getIdNodo(i);
//	nodoHijoIzquierdo -> agregarIdNodo(idNodoActual);
//
//	int j;
//	for (j = i + 1; j < raiz -> getCantidadElementos(); j++) {
//		claveElementoActual = ((NodoIndice*) raiz) -> getClaveElemento(j);
//		idNodoActual = ((NodoIndice*) raiz) -> getIdNodo(j);
//
//		nodoHijoDerecho -> agregarClaveElemento(claveElementoActual);
//		nodoHijoDerecho -> agregarIdNodo(idNodoActual);
//	}
//
//	/* Falta pasar el ultimo idNodo, pues siempre hay uno mas que clavesElementos */
//	idNodoActual = ((NodoIndice*) raiz) -> getIdNodo(j);
//	nodoHijoDerecho -> agregarIdNodo(idNodoActual);
//
//	claveAPromover = ((NodoIndice*) raiz)->getClaveElemento(elementoMedio);
//	raiz = new NodoIndice(cantidadNiveles, 0, Raiz);
//}

//void Arbol::agregarIdsNodosNuevosYclaveElemento(int idNodoHijoIzquierdo, int idNodoHijoDerecho, int claveElemento) {
//	((NodoIndice*) raiz) -> agregarIdNodo(idNodoHijoIzquierdo);
//	((NodoIndice*) raiz) -> agregarIdNodo(idNodoHijoDerecho);
//	((NodoIndice*) raiz) -> agregarClaveElemento(claveElemento);
//}

//void Arbol::actualizarNodosContiguos(INodo* nodoHijoIzquierdo, INodo* nodoHijoDerecho) {
//	((NodoHoja*)nodoHijoIzquierdo) -> setNodoSiguiente(nodoHijoDerecho -> getId());
//	((NodoHoja*)nodoHijoDerecho) -> setNodoAnterior(nodoHijoIzquierdo -> getId());
//}

void Arbol::solucionarOverflow() {
	INodo* nodoHijoIzquierdo;

	INodo* nodoHijoDerecho;

	nodoHijoDerecho = FactoryNodo::factory(this->cantidadNiveles);

	INodo *nuevaRaiz = FactoryNodo::factory(this->cantidadNiveles + 1);

	int idNodoHijoIzquierdo = nuevaRaiz -> getId();

	nuevaRaiz -> setId(idRaiz);

	nodoHijoIzquierdo = this -> raiz;

	nodoHijoIzquierdo -> setId(idNodoHijoIzquierdo);

	this -> raiz = nuevaRaiz;

	int claveAPromover;

	nodoHijoIzquierdo -> partirNodoEnOverflow(claveAPromover, nodoHijoDerecho);

	((NodoIndice*) this->raiz)->agregarClaveElemento(claveAPromover);

	((NodoIndice*) this->raiz)->agregarIdNodo(idNodoHijoIzquierdo);

	((NodoIndice*) this->raiz)->agregarIdNodo(nodoHijoDerecho->getId());

	nodoHijoIzquierdo->serializar();

	nodoHijoDerecho->serializar();

	delete nodoHijoIzquierdo;

	delete nodoHijoDerecho;

	/* Se suma un nivel al arbol */
	cantidadNiveles++;

}

void Arbol::solucionarUnderflow() {
	int claveNuevaRaiz;
	INodo* nuevaRaiz;

	/* Si la cantidad de niveles es mayor que uno, la raiz es indice, y si tiene solo un hijo entonces la raiz nueva
	 * es el unico hijo de la raiz original */

	if ((cantidadNiveles > 1) && (((NodoIndice*) raiz) -> getCantidadElementos() == 0)) {

		INodo *raizVieja = this->raiz;
		claveNuevaRaiz = ((NodoIndice*) raiz)->getIdNodo(0);
		nuevaRaiz = FactoryNodo::factory(this->cantidadNiveles-1,claveNuevaRaiz);
		raiz = nuevaRaiz;
		raiz->setId(idRaiz);
		delete raizVieja;
		cantidadNiveles--;
		// TODO: No se si hay que deletear la raiz original!!!



	}
}

void Arbol::setNombreArchivo(char* nombre){

	Archivo::setNombre(nombre);
}


void Arbol::insertarElemento(Elemento* elemento)
{
	if (elemento -> getTamanio() < (tamanioMaximoNodo * porcentajeCargaMaxima))
	{
		try
		{
			if (raiz -> insertarElemento(elemento))
			{
				raiz -> serializar();
			}
		}
		catch (ElementoExistenteException ex)
		{
			ex.mostrarError();
		}
		catch (OverflowException ex)
		{
			solucionarOverflow();

			raiz -> serializar();
		}
	}
	else
	{
		cout<<"Elemento de tamaño mayor a la mitad del tamaño maximo de nodo"<<endl;
	}
}

void Arbol::quitarElemento(int clave) {
	try
	{
		if (raiz -> quitarElemento(clave))
		{
			raiz -> serializar();
			if (elementoActual!=NULL){

				delete elementoActual;

				elementoActual=NULL;

				if (nodoActual!=NULL){

					delete nodoActual;

					nodoActual=NULL;
				}
			}
		}
	}
	catch (ElementoInexistenteException ex)
	{
		ex.mostrarError();
	}
	catch (UnderflowException ex)
	{
		solucionarUnderflow();

		raiz -> serializar();
	}

}

void Arbol::modificarElemento(int clave, string* nuevoValor)
{
	int tamanioNuevoElemento = sizeof(clave) + sizeof(int) + (sizeof(char)*nuevoValor->length());

	if (tamanioNuevoElemento < (tamanioMaximoNodo * porcentajeCargaMaxima))
	{
		try
		{
			if (raiz -> modificarElemento(clave, nuevoValor))
			{
				raiz -> serializar();
			}
		}
		catch (NodoVacioException ex)
		{
			ex.mostrarError();
		}
		catch (ElementoInexistenteException ex)
		{
			ex.mostrarError();
		}
		catch (OverflowException ex)
		{
//			ex.mostrarError();

			solucionarOverflow();

			raiz -> serializar();
		}
		catch (UnderflowException ex)
		{
//			ex.mostrarError();

			solucionarUnderflow();

			raiz -> serializar();
		}
	}
	else
	{
		cout<<"Valor de tamaño mayor a la mitad del tamaño maximo de nodo"<<endl;
	}
}

Elemento* Arbol::buscarElemento(int clave) {
	Elemento* aBuscar;

	try
	{
		aBuscar = raiz -> buscarElemento(clave);
	}
	catch (NodoVacioException ex)
	{
		ex.mostrarError();
	}
	catch (ElementoInexistenteException ex)
	{
		cout<<"El Elemento "<<ex.getElemento()<<" no existe ni exite un elemento con clave mayor"<<endl;
	}

	return aBuscar;
}

Elemento* Arbol::primerElemento() {
	return this -> buscarElemento(0);
}

void Arbol::setNodoActual(NodoHoja* nodo)
{
	delete this -> nodoActual;
	this -> nodoActual = NULL;
	this -> nodoActual = nodo -> clonar();
}

Elemento* Arbol::getElementoActual()
{
	if (elementoActual == NULL)
	{
		return primerElemento();
	}
	else
	{
		return this -> elementoActual;
	}
}

void Arbol::setElementoActual(Elemento* elemento)
{
	delete this -> elementoActual;
	this -> elementoActual = NULL;
	this -> elementoActual =  elemento -> clonar();
}

Elemento* Arbol::getElementoSiguiente()
{
	if (elementoActual == NULL)
	{
		return primerElemento();
	}
	else
	{
		Elemento* aRetornar;

		try
		{
			aRetornar = nodoActual -> getElementoSiguiente();
		}
		catch (NodoVacioException ex)
		{
			ex.mostrarError();
		}
		catch (ElementoInexistenteException ex)
		{
			cout<<"El elemento "<<this -> elementoActual -> getClave()<<" no tiene siguiente"<<endl;
		}

		return aRetornar;
	}
}

Elemento* Arbol::getElementoAnterior()
{
	if (elementoActual == NULL)
	{
		return primerElemento();
	}
	else
	{
		Elemento* aRetornar;

		try
		{
			aRetornar = nodoActual -> getElementoAnterior();
		}
		catch (NodoVacioException ex)
		{
			ex.mostrarError();
		}
		catch (ElementoInexistenteException ex)
		{
			cout<<"El elemento "<<this -> elementoActual -> getClave()<<" no tiene anterior"<<endl;
		}

		return aRetornar;
	}
}

void Arbol::print()
{
	ofstream archivoSalida("ListadoArbol.txt");

	archivoSalida << "Tamanio Bloque: " << tamanioMaximoNodo << " Factor overflow: " << tamanioMaximoNodo*porcentajeOverflow << " Factor underflow " << tamanioMaximoNodo*porcentajeUnderflow << " Cantidad de Niveles " << this->cantidadNiveles << "\n\n";
	this->raiz->printNodo(&archivoSalida,this->cantidadNiveles);

	archivoSalida.close();

}

void Arbol::hidratar(){

	std::stringstream *controlArchivo;

	short hayElementoActual;

	int claveElementoActual;

	int idNodoActual;

	controlArchivo = Archivo::getInstance()->read(0);

	controlArchivo->seekg(0);

	controlArchivo->read(reinterpret_cast<char*>(&this->idRaiz),sizeof(this->idRaiz));

	controlArchivo->read(reinterpret_cast<char*>(&this->cantidadNiveles),sizeof(this->cantidadNiveles));

	controlArchivo->read(reinterpret_cast<char*>(&hayElementoActual),sizeof(hayElementoActual));

	if (hayElementoActual==1){

		controlArchivo->read(reinterpret_cast<char*>(&claveElementoActual),sizeof(claveElementoActual));

		controlArchivo->read(reinterpret_cast<char*>(&idNodoActual),sizeof(idNodoActual));


		this->nodoActual = new NodoHoja(idNodoActual);

		this->nodoActual->hidratar();

		for (int i=0; i<1; i++){

			if (claveElementoActual == this->nodoActual->getElemento(i)->getClave()){

				this->elementoActual = this->nodoActual->getElemento(i)->clonar();
				break;
			}
		}
	}
	else{

		this->elementoActual =NULL;
		this->nodoActual = NULL;
	}

	this->raiz = FactoryNodo::factory(this->cantidadNiveles,this->idRaiz);

	delete controlArchivo;

}

void Arbol::serializar(){

	std::stringstream *controlArchivo = new std::stringstream();

	controlArchivo->write(reinterpret_cast<char*>(&this->idRaiz),sizeof(this->idRaiz));

	controlArchivo->write(reinterpret_cast<char*>(&this->cantidadNiveles),sizeof(this->cantidadNiveles));

	short hayElementoActual=0;

	if (this->elementoActual!=NULL){

		hayElementoActual = 1;

		controlArchivo->write(reinterpret_cast<char*>(&hayElementoActual),sizeof(hayElementoActual));

		int claveElementoActual = this->elementoActual->getClave();

		controlArchivo->write(reinterpret_cast<char*>(&claveElementoActual),sizeof(claveElementoActual));

		int idNodoActual = this->nodoActual->getId();

		controlArchivo->write(reinterpret_cast<char*>(&idNodoActual),sizeof(idNodoActual));
	}
	else{

		controlArchivo->write(reinterpret_cast<char*>(&hayElementoActual), sizeof(hayElementoActual));
	}

	Archivo::getInstance()->write(controlArchivo,0);

}

